package com.cee.auditlog.handler;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement;
import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser;

import com.cee.auditlog.bean.AuditLog;
import com.cee.auditlog.process.AuditLogProcess;
import com.cee.util.BeanUtil;
import com.cee.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class SQLServerInsertHandler implements SQLHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(SQLServerInsertHandler.class);
    
    private String sql;
    private String tableName;
    private List<Map<String, Object>> rowDataList;
    private SQLStatement sqlStatement;
    private List<String> columnList = new ArrayList<>();
    private String rowId;

    @Override
    public void preHandle(MappedStatement mappedStatement, Object parameterObject) {
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);

        this.sql = boundSql.getSql();
//        logger.info("sql: {}", sql);
        
        if (sql.toUpperCase().indexOf("INSERT") == -1) {
            logger.info("skip the sql[{}], it doesn't contain insert clause.", sql);
            return;
        }
        
        this.sqlStatement = new SQLServerStatementParser(sql).parseInsert();
        
        SQLServerInsertStatement sqlInsertStatement = (SQLServerInsertStatement)sqlStatement;
        tableName = sqlInsertStatement.getTableName().toString();
        if (StringUtils.isNotBlank(tableName)) {
            
            if (sqlInsertStatement.getColumns().size() > 0) {
                for (int i = 0; i < sqlInsertStatement.getColumns().size(); i++) {
                    SQLExpr columnExpr = sqlInsertStatement.getColumns().get(i);
                    columnList.add(SQLUtils.toSQLServerString(columnExpr));
                }
            }
            
            rowDataList = new ArrayList<>();
            Map<String, Object> valueOfColumnName = new HashMap<>();
                
            for(int i = 0; i < columnList.size(); i++) {
                String columnName = columnList.get(i);
                String fieldName = StringUtil.underlineToCamel2(columnName.toLowerCase());
                try {
                    Object value = BeanUtil.getPropertyValue(parameterObject, fieldName);
                    valueOfColumnName.put(columnName.toUpperCase(), value);
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            
            rowDataList.add(valueOfColumnName);
            
        }
        
    }

    @Override
    public void setRowId(String rowId) {
        this.rowId = rowId;
    }

    @Override
    public void postHandle(AuditLogProcess auditLogProcess) {
        logger.info("rowDataList.size: {}", rowDataList.size());
        
        Map<String, List<AuditLog>> columnLogsOfRow = new HashMap<>();
        int i = 0;
        for(Map<String, Object> rowData : rowDataList) {
            
            List<AuditLog> auditLogList = new ArrayList<>();
            for (Entry<String, Object> entry : rowData.entrySet()) {
                String columnName = entry.getKey();
                Object newValue = entry.getValue();
                
                AuditLog auditLog = new AuditLog();
                auditLog.setTableName(tableName);
                auditLog.setActionCode(SqlCommandType.UPDATE.toString());
                auditLog.setRowId(rowId);
                auditLog.setColumnName(columnName);
                auditLog.setOldValue(null);
                auditLog.setNewValue(BeanUtil.format(newValue));
                auditLogList.add(auditLog);
            }
            
            columnLogsOfRow.put(String.valueOf(i), auditLogList);
            i++;
        }
        
        auditLogProcess.process(tableName, SqlCommandType.INSERT.toString(), columnLogsOfRow);
    }
    
}
